
module("wikistorage", package.seeall)

require("cosmo")

SAVE_TEMPLATE = [=[
$dofields[[$field=[$eq[$value]$eq]
]]
]=]


ORDERED_FIELDS = { 
   "title", "author", "category", "summary",
   "minor", "metapage", "content" 
}

SIMPLE_FIELDS = { 
   title     = true,
   author    = true,
   category  = true,
   summary   = true,
   minor     = true,
   content   = true,
   metapage  = true,
   page_name = true,
   version   = true,
   prev_version = true,
   raw       = true,
   history   = true,
   name      = true,
}


WikiStorage = {

   storage = nil,
   environment = {},

   is_extra_field = function (self, field) 
      return not SIMPLE_FIELDS[field]
   end,

   new = function(self, storage)
      obj = {}
      obj.storage = storage
      setmetatable(obj, self)
      self.__index = self
      return obj         
   end,

   load_page = function (self, page_name, version)
      raw = self.storage:load_object(page_name, version)
      local new_env = {}
      for i,v in ipairs(self.environment) do
	 new_env[i] = v
      end
      local page = self:load_config(raw, new_env)
      page.version = version
      page.name = page_name
      page.raw = raw
      return page
   end,

   page_exists = function(self, page_name) 
       return self.storage:object_exists(page_name)
   end,

   save_page = function (self, page_name, page)
      -- page could be a table or a string
      if not page then
	 error("WikiPage: trying to save a nil page") 
      elseif type(page) == "table" then 
	 raw_page = cosmo.fill(SAVE_TEMPLATE, 
	    { dofields = function () 
		 for i, field in ipairs(ORDERED_FIELDS) do
		    cosmo.yield { 
		       field= field,
		       eq = "=================================",
		       value = page[field] or ""
		    }
		 end
	      end 
	    }
	 )
      elseif type(page) == "string" then 
	 raw_page = page
      end
      self.storage:save_object(page_name, raw_page)

   end,

   get_page_history = function(self, page_name, date_prefix)

      local history = self.storage:get_object_history(page_name, date_prefix)

      local cur_version = history[#history]
      local prev_version = nil

      if #history > 1 then
         prev_version = history[#history-1]
      end

      result = { all = history, 
	         current = cur_version, 
                 previous = prev_version,
	         page_name = page_name,
	         class = self,
                 load = function(hist, version) 
		    local obj = self:load_page(hist.page_name, version)
		    obj.history = hist
		    return obj
		 end,
                 load_latest = function(hist) 
                    return result.load(hist, result.current)
		 end,
	      }

      return result
   end,

   get_history = function(self, page_name, num_items, date_prefix)
      local all_history = {}
      local page_table = {}
      if page_name then
         pages = {page_name}
      else
         pages = self.storage:list_object_ids()
      end

      for i, p in ipairs(pages) do
         if p:len() > 2 then
            for j, v in ipairs(self:get_page_history(p, date_prefix).all) do 
	       all_history[#all_history+1] = {v, p}
            end
         end
      end

      function lessthan(a,b)
         return a[1] > b[1]
      end
      table.sort(all_history, lessthan)
      
      local short_history = {}
      for i=1, num_items do
         if not all_history[i] then break end
         local v, p = unpack(all_history[i])
         local page = self:load_page(p,v)
         table.insert(short_history, page)
      end
      
      return short_history
   end,

   load_page_or_new = function (self, page_name, version) 

      local page = { 
	 name = page_name,
	 page_name = page_name,
	 version   = "New Page",
	 author    = nil, 
	 title     = page_name, 
	 name      = page_name,
	 content   = "", 
	 minor     = "",
	 history   = {all={}},
	 actions   = {},
	 raw       = "",
      }

      if self:page_exists(page_name) then
	 page.history = self:get_page_history(page_name)
	 page.prev_version = ""
	 if version then
	    page.prev_version = nil  --::TODO::
	    page.version = version
	 else 
	    page.version = page.history.current
	    page.prev_version = page.history.previous
	 end

	 for k, v in pairs(page.history:load(page.version)) do
	    page[k] = v
	 end
      end

      return page
   end,

   get_config = function(self, page_name, environment)
      if not environment then
	 environment = {}
      end
      local config = {CONFIG_PAGE_NAME = page_name}
      local config_page = self:load_page_or_new(config.CONFIG_PAGE_NAME)
      return self:load_config(config_page.content, environment)
   end,


   load_config = function (self, text, environment)
      if text then 
	 f,e = loadstring(text)
	 if e then 
            return {}, "can't parse lua code in a page:\n"
	                .. e .. "\nLua code: " .. text
	 end
      end
      if f then 
	 setfenv(f, environment)
	 x = f()
	 results = {}
	 for k, v in pairs(getfenv(f)) do
	    results[k] = v
	 end
	 return results 
      end
      return nil
   end

}


local test = function () 
   require("wikifilestorage")
   wfs = WikiFileStorage("data/")
   storage = WikiPage:new(wfs)

   print(storage.dir)
   history = storage:get_page_history("Kepler")
   print(history.current)
   print(history.previous)
   page = history:load_latest()

   print(page.content)

end

open = function(storage) return WikiStorage:new(storage) end

-- test()

